package com.jml.jdk8.stream;

import com.jml.jdk8.base.Student;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @Author: J
 * @DATE: 2022-04-15 16:30
 */
public class StreamTest {
    @Test
    public void t1() {
        Object[] objects = {};
        Stream<Object> stream = Arrays.stream(objects);

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        Integer reduce = list.stream().map(a -> a * 2).reduce(0, (a, b) -> a + b);

        Comparator<Integer> sum = Integer::sum;
    }

    @Test
    public void t2() {
        /*//打印数组元素
        IntStream intStream = IntStream.of(new int[]{5, 6, 7});
        intStream.forEach(System.out::println);
        //
        IntStream.of(new int[]{5, 6, 7}).forEach(System.out::println);*/
        //打印[3,8}，前闭后开
        IntStream range = IntStream.range(3, 8);
        range.forEach(System.out::println);
        //
        IntStream.range(3, 8).forEach(System.out::println);
        //打印[3,8]，前闭后闭
        IntStream.rangeClosed(3, 8).forEach(System.out::println);
    }

    @Test
    public void t3() {
        Stream<String> stream = Stream.of("hello", "world", "helloworld");
        //给定一个整型，返回一个值
        //  <A> A[] toArray(IntFunction<A[]> generator);
        //因为toArray这个返回就是A[]数组
        // 因为这个stream里面是String，所以返回数组也就是String类型的
        //a就是stream中的各个元素
        String[] stringArray = stream.toArray(a -> new String[a]);
        Arrays.asList(stringArray).forEach(System.out::println);
        System.out.println("1----------------------------------------------------------------------------");

        //使用方法引用修改
        Stream<String> stream1 = Stream.of("hello", "world", "helloworld");
        String[] strings = stream1.toArray(String[]::new);
        Arrays.asList(strings).forEach(System.out::println);
        System.out.println("2----------------------------------------------------------------------------");

        //流转化为list
        Stream<String> stream2 = Stream.of("hello", "world", "helloworld");
        List<String> collect = stream2.collect(Collectors.toList());
        System.out.println(collect);
        System.out.println("3----------------------------------------------------------------------------");

        //流转化为list详细过程
        Stream<String> stream3 = Stream.of("hello", "world", "helloworld");
    /*  第二个参数就已经实现了，第三个参数的表达式可以随意写
        <R> R collect(
      Supplier<R> supplier,
          --不接受参数返回一个值，最后这里返回一个list，因为我们写得值就是list
      BiConsumer<R, ? super T> accumulator,
          --接受2个参数不返回值，一个参数是上一个的list一个参数是stream中的值
      BiConsumer<R, R> combiner
           --接受2个参数不返回值，一个参数是一个中list
      );
    theList就是第一个参数创建的ArrayList，
    item:就是Stream中的每一个元素
    theList2和theList1：就是将上一步得到的list放入一个新的list中(theList2和theList1不分先后顺序)
    */
        List<String> collect1 = stream3.collect(
                () -> new ArrayList(), //Supplier<R> supplier,
                (theList, item) -> theList.add(item), //BiConsumer<R, ? super T> accumulator,
                (theList1, theList2) -> theList1.addAll(theList2) //BiConsumer<R, R> combiner
        );
        System.out.println(collect1);
        System.out.println("4----------------------------------------------------------------------------");

        //流转化为list详细过程，使用方法引用
        Stream<String> stream4 = Stream.of("hello", "world", "helloworld");
        List<String> collect2 = stream4.collect(
                ArrayList::new, //Supplier<R> supplier,
                ArrayList::add,//BiConsumer<R, ? super T> accumulator,
                ArrayList::addAll//BiConsumer<R, R> combiner
        );
        System.out.println(collect2);
        System.out.println("5----------------------------------------------------------------------------");


/**
 * 转化为字符串
 */
        Stream<String> stream5 = Stream.of("hello", "world", "helloworld");
        String s = stream5.collect(() -> new StringBuilder(),
                (s3, item) -> s3.append(item),
                (s1, s2) -> s2.toString()
        ).toString();
        System.out.println(s);
/**
 * 转化为字符串--方法引用
 */
        Stream<String> stream7 = Stream.of("hello", "world", "helloworld");
        String s7 = stream7.collect(StringBuilder::new,
                StringBuilder::append,
                (s1, s2) -> s2.toString()
        ).toString();
        System.out.println(s7);
/**
 * 转化为字符串，并且转化为大写字母
 */
        Stream<String> stream6 = Stream.of("hello", "world", "helloworld");
        String s1 = stream6.collect(() -> new StringBuilder(),
                (s3, item) -> s3.append(item.toUpperCase()),
                (s0, s2) -> s2.toString()
        ).toString();
        System.out.println(s1);
        System.out.println("6----------------------------------------------------------------------------");

/**
 * 不使用Collectors.toList()，使用Collectors.toCollection()
 * Collectors.toCollection()这个可以自定义返回类型，默认是ArrayList,也可以自定义
 * Collectors.toList()的返回类型是写死的
 */
        Stream<String> stream8 = Stream.of("hello", "world", "helloworld", "hello");
        List<String> list = stream8.collect(Collectors.toCollection(ArrayList::new));
        System.out.println(list);
        Stream<String> stream9 = Stream.of("hello", "world", "helloworld", "hello");
        Set<String> list1 = stream9.collect(Collectors.toCollection(
                () -> {
                    return new HashSet<>();
                }
        ));
        Set<String> list00 = stream9.collect(Collectors.toCollection(HashSet::new));
        System.out.println(list1);
        System.out.println("7----------------------------------------------------------------------------");

/**
 * 使用Collectors.joining()生成字符串
 * 把元素拼接为字符串，按照遇到的顺序
 */
        Stream<String> stream10 = Stream.of("hello", "world", "helloworld", "hello");
        String collect11 = stream10.collect(Collectors.joining());
        System.out.println(collect11);
        System.out.println("8----------------------------------------------------------------------------");
    }

    @Test
    public void t4() {
        List<String> list = Arrays.asList("hello", "world", "helloworld");
        List<String> collectList1 = list.stream().map(str -> str.toUpperCase()).collect(Collectors.toList());
        System.out.println(collectList1);
        //不使用方法引用
        List<String> collectList = list.stream().map(String::toUpperCase).collect(Collectors.toList());
        System.out.println(collectList);

        //
        Student student1 = new Student("zhangsan", 100);
        Student student2 = new Student("lisi", 90);
        Student student3 = new Student("wangwu", 90);
        Student student4 = new Student("zhaoliu", 80);
        List<Student> students1 = Arrays.asList(student1, student2, student3, student4);
        List<String> collect = students1.stream().map(a -> {
            a.setName(a.getName() + "aa");
            return "xxx";
        }).collect(Collectors.toList());
        System.out.println(collect);
        System.out.println(students1);
    }

    @Test
    public void flatMap() {
        //进行平方，然后合成一个大的List
        Stream<List<Integer>> stream = Stream.of(Arrays.asList(1), Arrays.asList(1, 2), Arrays.asList(1, 2, 3));
        //flatMap(theList -> theList.stream()) 获取流里面的list然后根据list获取stream，
        //map(item -> item * item)把所有的值进行平方操作
        List<Integer> listAll = stream
                .flatMap(theList -> theList.stream())
                .map(item -> item * item)
                .collect(Collectors.toList());
        System.out.println(listAll);
    }

    /**
     * Stream有of方法获取流
     * 有empty方法获取一个空流
     * 有concat方法将两个流拼接到一块获取一个新的流
     * 有generate方法,参数是Supplier,不接受参数返回值
     * 有iterate方法
     */
    @Test
    public void stream的generate方法() {
        Stream<String> stream1 = Stream.generate(() -> UUID.randomUUID().toString());
        //函数引用
        Stream<String> stream = Stream.generate(UUID.randomUUID()::toString);
        //findFirst就是找到第一个对象，不对后面进行处理
        Optional<String> first = stream.findFirst();
        //Optional的正确使用需要先进性判断为空的操作
        first.ifPresent((a) -> System.out.println(first.get()));
        first.ifPresent(System.out::println);
    }

    /**
     * final T seed, final UnaryOperator<T> f
     * UnaryOperator  extends Function<T, T>
     * 返回一个无限的串行的有序的stream
     */
    @Test
    public void stream的iterate方法() {
        //初始值为1，后面的每个元素都是前一个元素+2,
        //如果不加条件就会成为一个无线流，生成无穷多个元素
        //所以需要加限制条件limit来限制产生多少个。
        Stream.iterate(1, item -> item + 2).limit(10).forEach(item -> System.out.println(item));
    }

    @Test
    public void iterate的注意事项() {
        //这行代码会打印出0 1 但是这个程序一直都不会停下来，永远执行下去
        //distinct不知道iterate只会输出0和1，所以就一直在帮iterate进行去重
        Stream.iterate(0, i -> (i + 1) % 2).distinct().limit(6).forEach(System.out::println);
        //这样就不会一直运行下去了iterate的输出是固定的值，后面的函数都不会等待
        Stream.iterate(0, i -> (i + 1) % 2).limit(6).distinct().forEach(System.out::println);
    }


    /**
     * 1,3,5,7,9,11
     * 找过流中大于2的元素然后将每个元素乘以2，然后忽略掉流中的前两个元素，
     * 然后在取流中的前两个元素，最后求出流中的元素综合
     * filter就是一个过滤,里面是predicate接口，满足条件就留下
     * mapToInt skip  limit  sum
     */
    @Test
    public void 案例() {
        Stream<Integer> stream = Stream.iterate(1, item -> item + 2).limit(6);
        int reduce1 = stream.filter(item -> item > 2)
                .map(a -> a * 2)
                .skip(2)
                .limit(2)
                .reduce(0, (a, b) -> a + b);
        //int reduce = stream.filter(item -> item > 2)
        //        .mapToInt(item -> item * 2)   //用mapToInt不用map，因为map最后拆箱的时候有性能损耗，jdk8极力避免这种损耗
        //        .skip(2) //忽略掉前面两个元素
        //        .limit(2) //取出前两个元素
        //        .sum(); //对所有的元素求和
        //这里用sum和reduce都可以，第一个参数并不是索引，而是第一个累加数
        //.reduce(0, Integer::sum);
        System.out.println(reduce1);
    }


    /**
     * 求出流中最小或者最大的元素然后打印
     */
    @Test
    public void 案例1() {
        Stream<Integer> stream = Stream.iterate(1, item -> item + 2).limit(6);
        stream.filter(item -> item > 2)
                .mapToInt(item -> item * 2)   //用mapToInt不用map，因为map最后拆箱的时候有性能损耗，jdk8极力避免这种损耗
                .skip(2) //忽略掉前面两个元素
                .limit(2) //取出前两个元素
                //取出最小的元素
                .min()
                //取出最大的元素
                //.max()
                .ifPresent(item -> System.out.println(item));
        //为什么min和max返回的是一个optional然而sum返回的是具体的值？
        //本质取决返回的这个值可不可能为空，因为对于sum如果里面为空sum得到的是一个0
    }

    /**
     * summaryStatistics取最大，最小，平均，总和，个数的值
     * summaryStatistics是总结小计
     */
    @Test
    public void summaryStatistics案例() {
        Stream<Integer> stream = Stream.iterate(1, item -> item + 2).limit(6);
        IntSummaryStatistics summaryStatistics = stream.filter(item -> item > 2)
                .mapToInt(item -> item * 2)
                .skip(2)
                .limit(2)
                .summaryStatistics();
        summaryStatistics.getAverage();
        System.out.println(summaryStatistics.getCount());
        summaryStatistics.getMax();
        summaryStatistics.getMin();
        summaryStatistics.getSum();
    }

    @Test
    public void distinct去重() {
        List<Integer> list = Arrays
                .asList(1, 2, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 2, 2, 3, 3, 5, 5);
        ArrayList<Integer> collect1 = list.stream().distinct()
                .collect(Collectors.toCollection(ArrayList::new));

        HashSet<Integer> collect = list.stream()
                .collect(Collectors.toCollection(HashSet::new));

        System.out.println(collect1);
    }


    /**
     * 短路
     * findFirst找出第一个元素
     * 找出列表长度为5的第一个单词,打印他的长度
     */
    @Test
    public void 短路() {
        List<String> list = Arrays.asList("hello", "world", "hello world");
        //打印第一个单词
        list.stream().filter(item -> item.length() == 5).limit(1).forEach(System.out::println);
        System.out.println("1---------------------------------------------------------------");

        //打印第一个单词的长度
        list.stream().mapToInt(item -> item.length()).filter(length -> length == 5).findFirst()
                .ifPresent(System.out::println);
        System.out.println("2---------------------------------------------------------------");

        //在mapToInt中的输出只会输出到第二个单词，后面的单词是不会输出，也就是不会遍历到后面的单词
        //mapToInt里面的遍历并不会将全部的元素遍历，因为后面接了一个filter条件和findFirst的条件只找第一个元素
        //当把这些操作全部放入容器后mapToInt中遍历只有找到第一个满足条件的元素就不会再进行遍历了。
        list.stream().mapToInt(item -> {
                    int length = item.length();
                    System.out.println(item);
                    return length;
                }).filter(length -> length == 5).findFirst()
                .ifPresent(System.out::println);
    }

    /**
     * 两个集合，得到结果是交叉的
     */
    @Test
    public void tt() {
        List<String> list1 = Arrays.asList("hello", "hi", "你好");
        List<String> list2 = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
        //不生成新的集合
        list1.forEach(item -> list2.forEach(it -> System.out.println(it + " " + item)));
        //生成新的集合,这里是数组的嵌套，所以用flatmap
        List<String> collect = list1.stream().flatMap(item -> list2.stream().map(item2 -> item + item2))
                .collect(Collectors.toList());
        collect.forEach(a -> System.out.println(a));

    }

    /**
     * groupingBy分组转化为map 参数是Function接口 Collectors.counting()得到个数
     * 1.将list转化为map,key为name，value是一个集合,集合中的对象是student
     * 2.将list转化为map,key为name，value是集合中元素的个数
     * 3.将list转化为map,根据name进行分组,value是list中score的平均值
     */
    @Test
    public void groupingBy分组转化为map() {
        Student student1 = new Student("zhangsan", 100);
        Student student2 = new Student("lisi", 90);
        Student student3 = new Student("wangwu", 90);
        Student student4 = new Student("zhaoliu", 80);
        List<Student> students = Arrays.asList(student1, student2, student3, student4);
        //Function<Student, String> getName = Student::getName;
        //Function<Student, String> getName1 = (Student s) -> s.getName();
        //根据name进行分组
        Map<String, List<Student>> nameMap = students.stream()
                .collect(Collectors.groupingBy(Student::getName));
        System.out.println(nameMap);
        //根据分数进行分组
        Map<Integer, List<Student>> scoreMap = students.stream()
                .collect(Collectors.groupingBy(s -> s.getScore()));
        //根据name进行分组,value是list的个数
        Map<String, Long> countMap = students.stream()
                .collect(Collectors.groupingBy(s -> s.getName(), Collectors.counting()));
        //根据name进行分组,value是list中score的平均值
        Map<String, Double> collect = students.stream()
                .collect(Collectors.groupingBy(
                        s -> s.getName(), Collectors.averagingDouble(s -> s.getScore())
                ));
    }

    /**
     * 分区：partition By
     * partitioningBy中的参数是predicate
     * 分组有多个情况，分区结果只有两个分区
     * 90以下一个区，90以上一个区，key只有true和false所以只有两个区
     */
    public void partitioningBy进行分区() {
        Student student1 = new Student("zhangsan", 100);
        Student student2 = new Student("lisi", 90);
        Student student3 = new Student("wangwu", 90);
        Student student4 = new Student("zhaoliu", 80);
        List<Student> students = Arrays.asList(student1, student2, student3, student4);
        //90以下一个区，90以上一个区
        Map<Boolean, List<Student>> collect = students.stream()
                .collect(Collectors.partitioningBy(s -> s.getScore() >= 90));
        //名字为zhaoliu的一个区，不是zhaoliu的一个区，value是总数
        Map<Boolean, Long> countMap = students.stream()
                .collect(Collectors.partitioningBy(s -> s.getName().equals("zhaoliu"), Collectors.counting()));
        //根据分数大于80的进行分区和小于等于80分区,value是list中score的平均值
        Map<Boolean, Double> collect001 = students.stream()
                .collect(Collectors.partitioningBy(
                        s -> s.getScore() > 80, Collectors.averagingDouble(s -> s.getScore())
                ));
    }


    /**
     * 找出集合中分数最低或者最大的然后打印出来
     * minBy指的是最小的通过comparator条件
     * maxBy指的是最大的通过comparator条件
     * averaging指的平局值
     * summingInt得到总数
     * summarizingInt得到总结，里面有各种值，最大最小平均个数，IntStream类中也有方法summaryStatistics也是得到总结
     * joining 拼接字符，可以加分隔符和前后缀
     */
    @Test
    public void minByMaxBy找出集合中分数最低同学() {
        Student student1 = new Student("zhangsan", 100);
        Student student2 = new Student("lisi", 90);
        Student student3 = new Student("wangwu", 90);
        Student student4 = new Student("zhaoliu", 80);
        List<Student> students = Arrays.asList(student1, student2, student3, student4);
        //minBy
        students.stream().collect(Collectors.minBy
                (Comparator.comparingInt(Student::getScore))).ifPresent(System.out::println);
        students.stream().collect(Collectors.minBy
                ((a, b) -> a.getScore() - b.getScore())).ifPresent(System.out::println);
        //maxBy
        students.stream().collect(Collectors.maxBy
                (Comparator.comparingInt(Student::getScore))).ifPresent(System.out::println);
        //平均值
        Double average = students.stream().collect(Collectors.averagingDouble(Student::getScore));
        Double average1 = students.stream().collect(Collectors.averagingDouble(a -> a.getScore()));
        System.out.println(average);
        System.out.println(average1);
        //总结summarizingInt-
        //总和summingInt
        Integer sum1 = students.stream().collect(Collectors.summingInt(Student::getScore));
        System.out.println("--" + sum1);
        //得到分数的统计信息
        IntSummaryStatistics sum = students.stream().collect(Collectors.summarizingInt(Student::getScore));
        System.out.println(sum.getSum());
        System.out.println(sum.getMax());
        System.out.println(sum.getAverage());
        System.out.println(sum.getCount());
        System.out.println(sum.getMin());
        //把名字拼接起来,如果要用分隔符拼接就直接在里面加
        //这个拼接是无缝拼接的。
        String joinName = students.stream().map(Student::getName).collect(Collectors.joining());
        //这个joining拼接是用,分开的
        String joinName加分割符 = students.stream().map(Student::getName).collect(Collectors.joining(","));
        //这个joining拼接是加了分隔符前缀和后缀
        String joinName加前后缀 = students.stream().map(Student::getName).collect(Collectors.joining(" 分隔符 ", "前缀", "后缀"));
        System.out.println(joinName加前后缀);
    }

    @Test
    public void 多次分组() {
        Student student1 = new Student("zhangsan", 100);
        Student student2 = new Student("lisi", 90);
        Student student3 = new Student("wangwu", 90);
        Student student4 = new Student("zhaoliu", 80);
        List<Student> students1 = Arrays.asList(student1, student2, student3, student4);
        Map<Integer, Map<String, List<Student>>> group2Map = students1.stream().collect(
                Collectors.groupingBy(Student::getScore, Collectors.groupingBy(Student::getName)));
    }


    /**
     * collectingAndThen方法有两个参数一个是Collector,一个是Function。
     * 先计算第一个参数，再计算第二个参数
     */
    @Test
    public void 分区和分组混合使用collectingAndThen() {
        Student student1 = new Student("zhangsan", 100);
        Student student2 = new Student("lisi", 90);
        Student student3 = new Student("wangwu", 90);
        Student student4 = new Student("zhaoliu", 80);
        List<Student> students1 = Arrays.asList(student1, student2, student3, student4);
        //两次分区
        Map<Boolean, Map<Boolean, List<Student>>> partitioningBy2 =
                students1.stream().collect(Collectors
                        .partitioningBy(s -> s.getScore() >= 90,
                                Collectors.partitioningBy(s -> s.getAge() >= 10)));

        //先分组，再分区，再分组
        Map<Integer, Map<Boolean, Map<String, List<Student>>>> partitioningByGroupingBy =
                students1.stream()
                        .collect(
                                Collectors.groupingBy(Student::getScore,
                                        Collectors.partitioningBy(s -> s.getScore() >= 90,
                                                Collectors.groupingBy(s -> s.getName()))));
        System.out.println(partitioningByGroupingBy);

        //统计分数大于80的学生，取出这个 个分组里的总的个数
        Map<Boolean, Long> collect =
                students1.stream()
                        .collect(Collectors.partitioningBy(s -> s.getScore() >= 80,
                                Collectors.counting()));
        System.out.println(collect);

        //先以名字分组，取出每个组中分数最小的学生
        //collectingAndThen:先进行结果集的收集，然后将收集到的结果集进行下一步的处理
        //collectingAndThen两个参数，第一个参数，是做处理，第二个参数是对第一个参数的结果进行处理。
        Map<String, Student> collectingAndThen =
                students1.stream()
                        .collect(Collectors.groupingBy
                                (Student::getName,
                                        Collectors.collectingAndThen
                                                (Collectors.minBy(Comparator.comparingInt(Student::getScore))
                                                        , Optional::get)));
        System.out.println("collectingAndThen--:   " + collectingAndThen);
    }


    @Test
    public void collectingAndThenExample() {
        //先把所有的值都乘2，然后获取平均值，然后对平均值乘方
        List<Integer> list = Arrays.asList(1, 2, 3, 4);
        Double result = list.stream().collect(Collectors.collectingAndThen(Collectors.averagingLong(v -> {
                    System.out.println("v--" + v + "--> " + v * 2);
                    return v * 2;
                }),
                s -> {
                    System.out.println("s--" + s + "--> " + s * s);
                    return s * s;
                }));
        System.out.println(result);
    }


    /**
     * 多次排序：按照长度排序，再按照字母排序
     * 字母排序：区分和不区分大小写
     * thenComparing什么时候起作用：当前一个比较器认为是相等的时候返回值为0，就会用thenComparing中的比较器，否则不会用
     */
    @Test
    public void ZZ00() {
        System.out.println("00");
        List<String> list = Arrays.asList("world", "nihao", "Nihao", "Hello", "hello", "welcome");
        //先按照长度排序，再按照字母排序
        Collections.sort(list, Comparator.comparingInt(String::length)
                //compareToIgnoreCase是忽略大小写按照字母排序
                .thenComparing((item1, item2) -> item1.compareToIgnoreCase(item2)));
        System.out.println("list: " + list);

        //先按照长度排序,不区分大小写按照字母排序,直接用
        List<String> list1 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list1, Comparator.comparingInt(String::length)
                .thenComparing(String.CASE_INSENSITIVE_ORDER));
        System.out.println("list1: " + list1);

        //先按照长度排序,不区分大小写排序,用lambda表达式
        List<String> list2 = Arrays.asList("world", "nihao", "Nihao", "Hello", "hello", "welcome");
        Collections.sort(list2, Comparator.comparingInt(String::length)
                .thenComparing((item1, item2) -> item1.compareToIgnoreCase(item2)));
        System.out.println("list2: " + list2);
//--上面这三种是同一种效果------------------------------------------------------------------------------------------------------

        //不区分大小写排序,用方法引用
        List<String> list3 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list3, Comparator.comparingInt(String::length)
                .thenComparing(Comparator.comparing(String::toLowerCase)));
        System.out.println("list3: " + list3);

        //不区分大小写排序，然后在进行倒序
        //thenComparing后面还可以一直接thenComparing
        //Comparator.comparing一个参数是function，两个参数是Comparator，
        //              使用完前面的比较器后再实现第二个参数的比较器
        //              第二个参数是对第一个参数的增强
        List<String> list4 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list4, Comparator.comparingInt(String::length)
                        .thenComparing(
                                Comparator.comparing(String::toLowerCase, Comparator.reverseOrder()))
                /*.thenComparing()*/);
        System.out.println("list4: " + list4);

        //先长度进行逆序(大-小)，再按字母不区分大小写逆序(z-a)排序
        //reversed本质就是reverseOrder，reverseOrder是静态方法，reversed是实例方法
        List<String> list5 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list5, Comparator.comparingInt(String::length).reversed()
                .thenComparing(Comparator.comparing(String::toLowerCase, Comparator.reverseOrder())));
        System.out.println("list5: " + list5);

        // 原始数据 "nihao" , "hello", "world", "welcome"  对字符长度进行逆序
        //"welcome, nihao ,  hello ,  world" ，  对nihao ,  hello ,  world字母进行逆序
        //"welcome, world  ,  hello , nihao"  因为上一步字母排序返回值都不为0，
        //                                所以最后一个thenComparing就没有生效
        //
        List<String> list6 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list6, Comparator.comparingInt(String::length).reversed()
                .thenComparing(Comparator.comparing(String::toLowerCase, Comparator.reverseOrder()))
                .thenComparing(Comparator.reverseOrder())
        );
        System.out.println("list6: " + list6);
    }


    /**
     * 这里不会执行onClose中的方法，因为没用用try (Stream<String> stream = list.stream();) 这个形式
     */
    @Test
    public void onClose1() {
        List<String> list = Arrays.asList("one", "two", "three", "four");
        //onClose方法只有在Stream的Close方法执行的时候才执行
        list.stream().onClose(() -> {
            System.out.println("aaa");
        }).onClose(() -> {
            System.out.println("bbb");
        }).forEach(System.out::println);
    }

    /**
     * onClose方法只有在Stream的Close方法执行的时候才执行
     * 即使其中一个onClose方法抛异常，也不会影响其他的onClose方法的执行，互相独立不依赖
     * 任何一个close关闭处理器出现异常，第一个异常会传递给调用者，其他的异常会被压制，
     * 在第一个异常之后会有Suppressed:其他的异常
     * 这里控制台只会显示NullPointerException异常，但是实际执行了第二个onClose的方法
     */
    @Test
    public void onClose2() {
        List<String> list = Arrays.asList("one", "two", "three", "four");
        try (Stream<String> stream = list.stream();) {
            stream.onClose(() -> {
                System.out.println("aaa");
                throw new NullPointerException("zzz");
            }).onClose(() -> {
                System.out.println("bbb");
                throw new ArithmeticException("zxzxz");
            }).forEach(System.out::println);
        }
    }

    /**
     * 假如把异常单独定义出来，抛的同一个实例异常，就看不到被压制的异常，不可能自己压制自己
     */
    @Test
    public void onClose3() {
        List<String> list = Arrays.asList("one", "two", "three", "four");
        NullPointerException nullPointerException = new NullPointerException("z1z1");
        try (Stream<String> stream = list.stream();) {
            stream.onClose(() -> {
                System.out.println("aaa");
                throw nullPointerException;
            }).onClose(() -> {
                System.out.println("bbb");
                throw nullPointerException;
            }).forEach(System.out::println);
        }
    }

    /**
     * 抛的不是同一个实例异常即使是同类型的异常，还是可以看到第二个异常被压制
     */
    @Test
    public void onClose4() {
        List<String> list = Arrays.asList("one", "two", "three", "four");
        try (Stream<String> stream = list.stream();) {
            stream.onClose(() -> {
                System.out.println("aaa");
                throw new NullPointerException("z1z1");
            }).onClose(() -> {
                System.out.println("bbb");
                throw new NullPointerException("再再");
            }).forEach(System.out::println);
        }
    }


    @Test
    public void t5() {
        List<Integer> collect = IntStream.range(3, 8).boxed().collect(Collectors.toList());

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        //reduce第一个参数N，表示从N开始累加
        Integer sum = list.stream().map(i -> i << 1).reduce(0, Integer::sum);
        Integer sum1 = list.stream().map(a -> a * 2).reduce(0, (a, b) -> a + b);
        Integer sum2 = list.stream().map(a -> a * 2).reduce((a, b) -> a + b).get();
        System.out.println(sum);
        System.out.println(sum2);
        System.out.println(sum1);
    }

    @Test
    public void t6() {
        Student student1 = new Student("zhangsan", 100);
        Student student2 = new Student("lisi", 90);
        Student student3 = new Student("wangwu", 90);
        Student student4 = new Student("zhaoliu", 80);
        List<Student> students1 = Arrays.asList(student1, student2, student3, student4);
        boolean b = students1.stream().allMatch(s -> s.getScore() == 80);
        boolean b1 = students1.stream().anyMatch(s -> s.getScore() == 80);
        boolean b2 = students1.stream().noneMatch(s -> s.getScore() == 80);
        System.out.println(b);
        System.out.println(b1);
        System.out.println(b2);
    }

    @Test
    public void t7() {
        Student student1 = new Student("zhangsan", 110);
        Student student2 = new Student("lisi", 100);
        Student student3 = new Student("wangwu", 90);
        Student student4 = new Student("zhaoliu", 80);
        List<Student> students1 = Arrays.asList(student1, student2, student3, student4);
        Optional<Student> collect = students1.stream().collect(Collectors.reducing((a, b) -> {
            a.setScore(a.getScore() + b.getScore());
            return a;
        }));
        Student student = collect.get();
        System.out.println(student);
    }

    @Test
    public void t8() {
        Student student1 = new Student("zhangsan", 110);
        Student student2 = new Student("lisi", 100);
        Student student3 = new Student("wangwu", 90);
        Student student4 = new Student("zhaoliu", 80);
        List<Student> students1 = Arrays.asList(student1, student2, student3, student4);
        List<Integer> collect = students1.stream().collect(Collectors.mapping(
                (a) -> {
                    return a.getScore();
                },
                Collectors.toList()
        ));
        System.out.println(collect);
    }

    @Test
    public void t9() {
    }

}
